package modelo.dominio.util;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

/**
 *Clase que descomprime el archivo
 *
 * @author GustavoCanul, NoeCob, ArandiLopez
 *
 */
public class Descompresor {

    private String rutaOrigen;
    private String rutaDestino;
    private String nombreArchivo;
    private FileOutputStream escritor;
    private FileInputStream lector;

    /**
     *Constructor que inicializa las rutas del archivo origen y destino 
     *
     * @param rutaOrigen
     * @param rutaDestino
     */
    public Descompresor(String nombreArchivo, String rutaOrigen, String rutaDestino) {

        this.rutaOrigen = rutaOrigen;
        this.rutaDestino = rutaDestino;
        this.nombreArchivo = nombreArchivo;


    }

    /**
     * Descomprime un archivo de extension .gan , generando la tabla de codigos,
     * y a su vez el nuevo archivo descompreso.
     *
     * @throws FileNotFoundException
     * @throws IOException
     */
    public void descomprimir() throws FileNotFoundException, IOException {

        String linea = "", registroAuxiliar = "", extension = "", codigos[];
        int tamano = 0, contBytes = 0, byteLeido = 0;
        long numeroBytes = 0;
        byte registroTablaBytes[];

        lector = new FileInputStream(rutaOrigen);

        //***** LEEMOS LA TABLA ***********************

        for (int i = 0; i < 4; i++) {
            numeroBytes += (lector.read() * Math.pow(256, i));
        }
        for (int i = 0; i < 4; i++) {
            extension += (char) lector.read();
            
        }

        tamano = lector.read();

        registroTablaBytes = new byte[tamano];
        codigos = new String[tamano];
        escritor = new FileOutputStream(rutaDestino + obtenerNombreArchivo(nombreArchivo) + extension);

        for (int i = 0; i < tamano; i++) {
            registroTablaBytes[i] = (byte) lector.read();
            codigos[i] = generaCodigoHuffman((int) lector.read(), lector.read());
        }

        do {
            byteLeido = lector.read();

            if (byteLeido != -1) {
                linea = generaASCII(byteLeido);

                for (int i = 0; i < linea.length(); i++) {
                    registroAuxiliar += linea.charAt(i);

                    for (int j = 0; j < codigos.length; j++) {

                        if (registroAuxiliar.equals(codigos[j]) && contBytes < numeroBytes) {
                            escritor.write(registroTablaBytes[j]);
                            contBytes++;
                            registroAuxiliar = "";
                            break;
                        }
                    }
                }
            }
        } while (byteLeido != -1);
        lector.close();
        escritor.close();

    }

    /**
     * Metodo que genera codigo Huffman para el byte determinado
     *
     * @param numero es el numero a convertir
     * @param noCero es la cantidad de ceros a a�adir
     * @return revertir 
     */
    private String generaCodigoHuffman(int numero, int noCero) {

        String bin = decimalAbinario(numero);
        String code = revertir(bin, bin.length() - 1);

        for (int i = 0; i < noCero; i++) {
            code += "0";
        }
        return revertir(code, code.length() - 1);
    }

    /**
     * Metodo que transforma el ASCII del byte leido a una linea de binarios.
     *
     * @param numero es el numero a convertir
     * @return la nueva cadena
     */
    private String generaASCII(int numero) {

        String bin = decimalAbinario(numero);
        String ascii = revertir(bin, bin.length() - 1);

        for (int i = 0; ascii.length() < 8; i++) {
            ascii += "0";
        }

        return revertir(ascii, ascii.length() - 1);

    }

    /**
     * Convierte un numero entero a una cadena que representa un numero binario.
     *
     * @param num 
     * @return revertir
     */
    private String decimalAbinario(int num) {

        String res = "";

        while (num >= 1) {
            res += (char) ((num % 2) + 48);
            num = num / 2;
        }
        return revertir(res, res.length() - 1);
    }

    /**
     * Invierte una cadena de texto
     *
     * @param operador 
     * @param indice e
     * @return La nueva cadena invertida
     */
    private String revertir(String operador, int indice) {
        if (indice < 0) {
            return "";
        } else {
            return "" + operador.charAt(indice) + revertir(operador, indice - 1);
        }
    }
/**
 * Obtiene el nombre del archivo
 * @param nombreCompleto
 * @return 
 */
    private String obtenerNombreArchivo(String nombreCompleto) {
        String salida = "";
        for (int i = 0; i < nombreCompleto.length(); i++) {
            if (nombreCompleto.charAt(i) == '.') {
                salida = nombreCompleto.substring(0, i);

                System.out.println(salida);
                return salida;
            }
        }
        return null;
    }

}
